using System;

namespace Engine.Shape
{
    internal class CPoint2D
    {
        #region Private Fields

        private float m_dCoordinate_X;
        private float m_dCoordinate_Y;
        private float m_dCoordinate_Z;

        #endregion Private Fields

        #region Public Constructors

        public CPoint2D(float xCoordinate, float yCoordinate, float zCoordinate)
        {
            m_dCoordinate_X = xCoordinate;
            m_dCoordinate_Y = yCoordinate;
            m_dCoordinate_Z = zCoordinate;
        }

        public CPoint2D()
        {
        }

        #endregion Public Constructors

        #region Public Properties

        public float X
        {
            set
            {
                m_dCoordinate_X = value;
            }
            get
            {
                return m_dCoordinate_X;
            }
        }

        public float Y
        {
            set
            {
                m_dCoordinate_Y = value;
            }
            get
            {
                return m_dCoordinate_Y;
            }
        }

        public float Z
        {
            set
            {
                m_dCoordinate_Z = value;
            }
            get
            {
                return m_dCoordinate_Z;
            }
        }

        #endregion Public Properties

        #region Public Methods

        public static bool SamePoints(CPoint2D Point1,
                    CPoint2D Point2)
        {
            double dDeff_X =
                Math.Abs(Point1.X - Point2.X);
            double dDeff_Y =
                Math.Abs(Point1.Y - Point2.Y);

            if ((dDeff_X < ConstantValue.SmallValue)
                && (dDeff_Y < ConstantValue.SmallValue))
                return true;
            else
                return false;
        }

        /// <summary>
        /// Sort points from Xmin->Xmax
        /// </summary>
        /// <param name="points"></param>
        public static void SortPointsByX(CPoint2D[] points)
        {
            if (points.Length > 1)
            {
                CPoint2D tempPt;
                for (int i = 0; i < points.Length - 2; i++)
                {
                    for (int j = i + 1; j < points.Length - 1; j++)
                    {
                        if (points[i].X > points[j].X)
                        {
                            tempPt = points[j];
                            points[j] = points[i];
                            points[i] = tempPt;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Sort points from Ymin->Ymax
        /// </summary>
        /// <param name="points"></param>
        public static void SortPointsByY(CPoint2D[] points)
        {
            if (points.Length > 1)
            {
                CPoint2D tempPt;
                for (int i = 0; i < points.Length - 2; i++)
                {
                    for (int j = i + 1; j < points.Length - 1; j++)
                    {
                        if (points[i].Y > points[j].Y)
                        {
                            tempPt = points[j];
                            points[j] = points[i];
                            points[i] = tempPt;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Distance between two points
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public double DistanceTo(CPoint2D point)
        {
            return Math.Sqrt((point.X - this.X) * (point.X - this.X)
                + (point.Y - this.Y) * (point.Y - this.Y));
        }

        public bool EqualsPoint(CPoint2D newPoint)
        {
            double dDeff_X =
                Math.Abs(m_dCoordinate_X - newPoint.X);
            double dDeff_Y =
                Math.Abs(m_dCoordinate_Y - newPoint.Y);

            if ((dDeff_X <= ConstantValue.SmallValue)
                && (dDeff_Y <= ConstantValue.SmallValue))
                return true;
            else
                return false;
        }

        /// <summary>
        /// To check whether the point is in a line segment
        /// </summary>
        /// <param name="lineSegment"></param>
        /// <returns></returns>
        public bool InLine(CLineSegment lineSegment)
        {
            bool bInline = false;

            double Ax, Ay, Bx, By, Cx, Cy;
            Bx = lineSegment.EndPoint.X + 0.0001;
            By = lineSegment.EndPoint.Y + 0.0001;
            Ax = lineSegment.StartPoint.X;
            Ay = lineSegment.StartPoint.Y;
            Cx = this.m_dCoordinate_X;
            Cy = this.m_dCoordinate_Y;

            double L = lineSegment.GetLineSegmentLength();
            double s = Math.Abs(((Ay - Cy) * (Bx - Ax) - (Ax - Cx) * (By - Ay)) / (L * L));

            if (Math.Abs(s - 0) < ConstantValue.SmallValue)
            {
                if ((SamePoints(this, lineSegment.StartPoint)) ||
                    (SamePoints(this, lineSegment.EndPoint)))
                    bInline = true;
                else if ((Cx < lineSegment.GetXmax())
                    && (Cx > lineSegment.GetXmin())
                    && (Cy < lineSegment.GetYmax())
                    && (Cy > lineSegment.GetYmin()))
                    bInline = false;
            }
            return bInline;
        }

        public bool PointInsidePolygon(CPoint2D[] polygonVertices)
        {
            if (polygonVertices.Length < 3) //not a valid polygon
                return false;

            int nCounter = 0;
            int nPoints = polygonVertices.Length;

            CPoint2D s1, p1, p2;
            s1 = this;
            p1 = polygonVertices[0];

            for (int i = 1; i < nPoints; i++)
            {
                p2 = polygonVertices[i % nPoints];
                if (s1.Y > Math.Min(p1.Y, p2.Y))
                {
                    if (s1.Y <= Math.Max(p1.Y, p2.Y))
                    {
                        if (s1.X <= Math.Max(p1.X, p2.X))
                        {
                            if (p1.Y != p2.Y)
                            {
                                double xInters = (s1.Y - p1.Y) * (p2.X - p1.X) /
                                    (p2.Y - p1.Y) + p1.X;
                                if ((p1.X == p2.X) || (s1.X <= xInters))
                                {
                                    nCounter++;
                                }
                            }  //p1.y != p2.y
                        }
                    }
                }
                p1 = p2;
            } //for loop

            if ((nCounter % 2) == 0)
                return false;
            else
                return true;
        }

        public override string ToString()
        {
            return "X: " + this.m_dCoordinate_X + " Y: " + this.m_dCoordinate_Y + " Z: " + this.m_dCoordinate_Z;
        }

        #endregion Public Methods
    }
}